package com.xms.sdk.query;

import com.xms.core.enums.AccessRightValue;
import com.xms.core.exception.XmsException;
import com.xms.datacore.SqlDescriptor;
import com.xms.datacore.provider.IDataRepositoryBase;
import com.xms.schema.entity.Attribute;
import com.xms.schema.extension.AttributeExt;
import com.xms.schema.service.entity.IEntityFinderService;
import com.xms.sdk.core.DynamicData;
import com.xms.sdk.core.RetrieverParameter;
import com.xms.sdk.core.query.*;
import com.xms.sdk.extensions.IQueryMetadataFinder;
import com.xms.sdk.extensions.QueryExpressionExt;
import com.xms.security.entity.RoleEntityPermission;
import com.xms.security.identity.ICurrentUser;
import com.xms.security.service.dataauth.IRoleEntityPermissionService;
import com.xms.utils.CollectionUtil;
import com.xms.utils.StringUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * <p>
 * 聚合表达式解析器
 * </p>
 *
 * @author migo
 * @since 2021/9/7
 */
@Service
public class AggregateExpressionResolver implements IAggregateExpressionResolver {
    private Logger _logger = LoggerFactory.getLogger(getClass());
    private IDataRepositoryBase _dataRepositoryBase;
    private IQueryResolverFactory _queryResolverFactory;
    private IQueryMetadataFinder _queryMetadataFinder;
    private IRoleEntityPermissionService _roleEntityPermissionService;
    private IEntityFinderService _entityFinderService;
    private ICurrentUser _currentUser;

    @Autowired
    public AggregateExpressionResolver(IQueryResolverFactory queryResolverFactory,
                                       IQueryMetadataFinder queryMetadataFinder,
                                       IDataRepositoryBase dataRepositoryBase,
                                       ICurrentUser currentUser,
                                       IRoleEntityPermissionService roleObjectAccessEntityPermissionService,
                                       IEntityFinderService entityFinderService) {
        _queryResolverFactory = queryResolverFactory;
        _queryMetadataFinder = queryMetadataFinder;
        _dataRepositoryBase = dataRepositoryBase;
        _currentUser = currentUser;
        _roleEntityPermissionService = roleObjectAccessEntityPermissionService;
        _entityFinderService = entityFinderService;
    }

    private RetrieverParameter buildParameters(QueryBase request) throws XmsException {
        RetrieverParameter parameters = RetrieverParameter.build(request, _currentUser).setIncludeNameField(true).setIgnorePermissions(false);
        //get entity permissions
        parameters.setEntityPermissions(getUserEntityPermissions(request, AccessRightValue.Read));
        return parameters;
    }

    private List<RoleEntityPermission> getUserEntityPermissions(QueryBase q, AccessRightValue access) throws XmsException {
        //get entity permissions
        if (_currentUser.isAdministrator())
            return null;
        List<UUID> rolesId = _currentUser.getRoles();
        if (CollectionUtil.isEmpty(rolesId)) {
            throw new XmsException("用户没有分配角色");
        }
        List<String> entityNames = q instanceof QueryExpression ? QueryExpressionExt.getAllEntityNames((QueryExpression) q, null) : new ArrayList<String>() {
            {
                add(q.EntityName);
            }
        };
        List<UUID> entIds = _entityFinderService.getByNames(entityNames).stream().map(x -> x.getEntityid()).collect(Collectors.toList());
        return _roleEntityPermissionService.getRolePermissions(entIds, rolesId, access);
    }

    @Override
    public List<DynamicData> execute(AggregateExpression agg) throws SQLException, XmsException {
        IQueryResolver queryResolver = _queryResolverFactory.getResolver(agg);
        SqlDescriptor sqlString = queryResolver.toSqlString(buildParameters(agg), _queryMetadataFinder.getAll(agg));
        List<String> selectFields = new ArrayList<>();
        for (AggregateExpressionField a : agg.AggregateFields) {
            selectFields.add(DataFieldExpressionHelper.getAggregationExpression(a.AggregateType, a.AttributeName) + " AS " + a.AttributeName);
        }
        String finalSqlString = String.format("SELECT %s FROM (%s) a", String.join(",", selectFields).toLowerCase(), sqlString.getFinalSql());

        printSql(finalSqlString, sqlString.getFinalArgs());
        return _dataRepositoryBase.query(new SqlDescriptor(finalSqlString, sqlString.getFinalArgs()));
    }

    @Override
    public List<DynamicData> execute(QueryExpression query, Map<String, AggregateType> attributeAggs, List<String> groupFields) throws SQLException, XmsException {
        IQueryResolver queryResolver = _queryResolverFactory.getResolver(query);
        query.ColumnSet.Columns.clear();
        query.ColumnSet.addColumns((List<String>) attributeAggs.keySet());
        query.ColumnSet.addColumns(groupFields);
        if (!query.Orders.isEmpty()) {
            query.Orders.clear();
        }
        QueryMetadataDescriptor metadataDescriptor = _queryMetadataFinder.getAll(query);
        SqlDescriptor sqlString = queryResolver.toSqlString(buildParameters(query), metadataDescriptor);
        List<String> selectFields = new ArrayList<>();
        //List<string> groupFields = new List<string>();
        for (String k : attributeAggs.keySet()) {
            selectFields.add(DataFieldExpressionHelper.getAggregationExpression(attributeAggs.get(k), k) + " AS " + k + attributeAggs.get(k).text());
        }
        List<String> nameFields = new ArrayList<>();
        for (String gf : groupFields) {
            Attribute attr = metadataDescriptor.attributeList.stream().filter(n -> n.getName().equalsIgnoreCase(gf)).findFirst().get();
            String nameField = AttributeExt.getNameField(attr, "");
            if (!gf.equalsIgnoreCase(nameField)) {
                nameFields.add(nameField);
            }
        }
        groupFields.addAll(nameFields);
        String finalSqlString = String.format("SELECT %s FROM (%s) a GROUP BY %s", String.join(",", Stream.concat(selectFields.stream(), groupFields.stream()).collect(Collectors.toList())).toLowerCase(), sqlString, String.join(",", groupFields).toLowerCase());

        printSql(finalSqlString, sqlString.getFinalArgs());
        return _dataRepositoryBase.query(new SqlDescriptor(finalSqlString, sqlString.getFinalArgs()));
    }

    @Override
    public List<DynamicData> groupingTop(int top, QueryExpression query, String groupField, OrderExpression order) throws XmsException, SQLException {
        IQueryResolver queryResolver = _queryResolverFactory.getResolver(query);
        String orderString = String.format(" ORDER BY %s.%s ", query.EntityName, order.AttributeName + (order.OrderType == OrderType.Descending ? " DESC" : ""));
        query.Orders.clear();
        SqlDescriptor sqlString = queryResolver.toSqlString(buildParameters(query), _queryMetadataFinder.getAll(query));
        String rowSql = String.format("ROW_NUMBER() OVER(PARTITION BY %s.%s %s) as RowNum", query.EntityName, groupField, orderString);
        String finalSqlString = sqlString.getFinalSql().replace("SELECT ", "SELECT " + rowSql + ",");
        finalSqlString = String.format("SELECT * FROM (%s) a ORDER BY RowNum", sqlString);

        printSql(finalSqlString, sqlString.getFinalArgs());
        return _dataRepositoryBase.query(new SqlDescriptor(finalSqlString, sqlString.getFinalArgs()));
    }

    private void printSql(String s, Object[] args) {
        _logger.info("finalsql is: {}", s);
        if (CollectionUtil.notEmpty(args))
            _logger.info("finalargs is: {}", StringUtil.join(args, ", "));
    }
}
